using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using SymbolIntegration.core.Expressions;
using SymbolIntegration.core.Expressions.Functions;

namespace SymbolIntegration.core.Helpers
{
    public class PluginIntegrator : IIntegrator
    {
        static private readonly List<IIntegrator> ms_Integrators = new List<IIntegrator>();

        static PluginIntegrator ()
        {
            var dlls = Directory.GetFiles("plugins", "*.dll").Select(Path.GetFullPath);
            var interfaceType = typeof (IIntegrator);
            foreach (var dll in dlls)
            {

                Assembly assembly = null;
                try
                {
                   assembly = Assembly.LoadFile(dll);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Failed to load assembly. Error: %s" + ex.Message+"." + ex);
                }
                if (assembly == null)
                {
                    continue;
                }

                var integrators = assembly.GetTypes()
                    .Where(type => interfaceType.IsAssignableFrom(type) && type != typeof(PluginIntegrator))
                    .Select(type => (IIntegrator) Activator.CreateInstance(type));
                ms_Integrators.AddRange(integrators);
            }
            ms_Integrators = ms_Integrators.Where(integrator => integrator != null).ToList();
            if (ms_Integrators.Count == 0)
            {
                Debug.WriteLine("Failed to found any integrators");
            }
        }

        public bool CanIntegrate(List<Expression> multipliers, ExpressionStatistic statistic, string variable)
        {
            return true;
        }

        public Expression TryIntegrateMultipliers(List<Expression> multipliers, string variable)
        {
            Expression multiplierExpr = new ConstantExpression(1);
            multiplierExpr = multipliers.Aggregate(multiplierExpr, (current, multiplier) => current*multiplier);
            var statistic = multiplierExpr.GetStatistic(variable);

            IIntegrator canIntegrateIntegrator = ms_Integrators
                .FirstOrDefault(integrator => integrator.CanIntegrate(multipliers, statistic, variable));

            Expression result = canIntegrateIntegrator == null ? new IntegrFunction(multiplierExpr, variable) : canIntegrateIntegrator.TryIntegrateMultipliers(multipliers, variable);
            return result;
        }
    }
}